<?php
namespace Goatherd\Content\Engine\Reader;

use Goatherd\Content\Engine\Resource\IResource;

/**
 * Commen resource container.
 *
 * May be used to call custom events or defer resource retrieval.
 */
class ResourceContainer
{
    const MODE_NONE = 0;
    const MODE_CLOSURE = 1;
    const MODE_RESOURCE = 2;

    /**
     *
     * @var integer
     */
    protected $_mode = self::MODE_NONE;

    /**
     *
     * @var mixed
     */
    protected $_container = null;

    /**
     *
     * @param \Closure $resource
     */
    public function fromClosure(\Closure $resource)
    {
        $this->validate();

        $this->_mode = self::MODE_CLOSURE;
        $this->_container = $resource;
    }

    /**
     *
     * @param IResource $resource
     */
    public function fromResource(IResource $resource)
    {
        $this->validate();

        $this->_mode = self::MODE_RESOURCE;
        $this->_container = $resource;
    }

    /**
     *
     * @return boolean
     */
    public function isEmpty()
    {
        return $this->_mode === self::MODE_NONE;
    }

    /**
     *
     * @throws Exception
     */
    public function validate()
    {
        if ( ! $this->isEmpty() ) {
            throw new Exception('Can not set resource container twice.');
        }
    }

    /**
     *
     * @throws Exception
     * @return IResource
     */
    public function getResource()
    {
        $resource = null;

        switch ($this->_mode) {
            case self::MODE_CLOSURE:
                // fall back to resource
                $resource = $this->_container;
                $this->_container = $resource();
                $this->_mode = self::MODE_RESOURCE;
            case self::MODE_RESOURCE;
                $resource = $this->_container;
                break;

            default:
                throw new Exception('Resource container is empty.');
        }

        return $resource;
    }

    /**
     *
     * @param \Closure|IResource|null    $obj    [=null]
     * @return ResourceContainer
     * @throws \InvalidArgumentException
     */
    public static function create($obj = null)
    {
        $rc = new static();
        if ($obj instanceof \Closure) {
            $rc->fromClosure($obj);
        } elseif ($obj instanceof IResource) {
            $rc->fromResource($obj);
        } elseif ($obj !== null) {
            throw new \InvalidArgumentException('ResourceContainer argument must be Closure, IResource or null.');
        }

        return $rc;
    }
}